﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;

namespace Common
{
	//Messenger from DevUI
	public class MessageBase
	{
		/// <summary>
		/// Initializes a new instance of the MessageBase class.
		/// </summary>
		public MessageBase()
		{
		}

		/// <summary>
		/// Initializes a new instance of the MessageBase class.
		/// </summary>
		/// <param name="sender">The message's original sender.</param>
		public MessageBase(object sender)
		{
			Sender = sender;
		}

		/// <summary>
		/// Initializes a new instance of the MessageBase class.
		/// </summary>
		/// <param name="sender">The message's original sender.</param>
		/// <param name="target">The message's intended target. This parameter can be used
		/// to give an indication as to whom the message was intended for. Of course
		/// this is only an indication, amd may be null.</param>
		public MessageBase(object sender, object target)
			: this(sender)
		{
			Target = target;
		}

		/// <summary>
		/// Gets or sets the message's sender.
		/// </summary>
		public object Sender
		{
			get;
			protected set;
		}

		/// <summary>
		/// Gets or sets the message's intended target. This property can be used
		/// to give an indication as to whom the message was intended for. Of course
		/// this is only an indication, amd may be null.
		/// </summary>
		public object Target
		{
			get;
			protected set;
		}
	}

	public class Messenger : IMessenger
	{

		private Dictionary<Type, List<WeakActionAndToken>> _recipientsOfSubclassesAction;
		private Dictionary<Type, List<WeakActionAndToken>> _recipientsStrictAction;

		/// <summary>
		/// Registers a recipient for a type of message TMessage. The action
		/// parameter will be executed when a corresponding message is sent.
		/// <para>Registering a recipient does not create a hard reference to it,
		/// so if this recipient is deleted, no memory leak is caused.</para>
		/// </summary>
		/// <typeparam name="TMessage">The type of message that the recipient registers
		/// for.</typeparam>
		/// <param name="recipient">The recipient that will receive the messages.</param>
		/// <param name="action">The action that will be executed when a message
		/// of type TMessage is sent.</param>
		public virtual void Register<TMessage>(object recipient, Action<TMessage> action)
		{
			Register(recipient, null, false, action);
		}

		/// <summary>
		/// Registers a recipient for a type of message TMessage.
		/// The action parameter will be executed when a corresponding 
		/// message is sent. See the receiveDerivedMessagesToo parameter
		/// for details on how messages deriving from TMessage (or, if TMessage is an interface,
		/// messages implementing TMessage) can be received too.
		/// <para>Registering a recipient does not create a hard reference to it,
		/// so if this recipient is deleted, no memory leak is caused.</para>
		/// </summary>
		/// <typeparam name="TMessage">The type of message that the recipient registers
		/// for.</typeparam>
		/// <param name="recipient">The recipient that will receive the messages.</param>
		/// <param name="receiveDerivedMessagesToo">If true, message types deriving from
		/// TMessage will also be transmitted to the recipient. For example, if a SendOrderMessage
		/// and an ExecuteOrderMessage derive from OrderMessage, registering for OrderMessage
		/// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
		/// and ExecuteOrderMessage to the recipient that registered.
		/// <para>Also, if TMessage is an interface, message types implementing TMessage will also be
		/// transmitted to the recipient. For example, if a SendOrderMessage
		/// and an ExecuteOrderMessage implement IOrderMessage, registering for IOrderMessage
		/// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
		/// and ExecuteOrderMessage to the recipient that registered.</para>
		/// </param>
		/// <param name="action">The action that will be executed when a message
		/// of type TMessage is sent.</param>
		public virtual void Register<TMessage>(object recipient, bool receiveDerivedMessagesToo, Action<TMessage> action)
		{
			Register(recipient, null, receiveDerivedMessagesToo, action);
		}

		/// <summary>
		/// Registers a recipient for a type of message TMessage.
		/// The action parameter will be executed when a corresponding 
		/// message is sent.
		/// <para>Registering a recipient does not create a hard reference to it,
		/// so if this recipient is deleted, no memory leak is caused.</para>
		/// </summary>
		/// <typeparam name="TMessage">The type of message that the recipient registers
		/// for.</typeparam>
		/// <param name="recipient">The recipient that will receive the messages.</param>
		/// <param name="token">A token for a messaging channel. If a recipient registers
		/// using a token, and a sender sends a message using the same token, then this
		/// message will be delivered to the recipient. Other recipients who did not
		/// use a token when registering (or who used a different token) will not
		/// get the message. Similarly, messages sent without any token, or with a different
		/// token, will not be delivered to that recipient.</param>
		/// <param name="action">The action that will be executed when a message
		/// of type TMessage is sent.</param>
		public virtual void Register<TMessage>(object recipient, object token, Action<TMessage> action)
		{
			Register(recipient, token, false, action);
		}

		/// <summary>
		/// Registers a recipient for a type of message TMessage.
		/// The action parameter will be executed when a corresponding 
		/// message is sent. See the receiveDerivedMessagesToo parameter
		/// for details on how messages deriving from TMessage (or, if TMessage is an interface,
		/// messages implementing TMessage) can be received too.
		/// <para>Registering a recipient does not create a hard reference to it,
		/// so if this recipient is deleted, no memory leak is caused.</para>
		/// </summary>
		/// <typeparam name="TMessage">The type of message that the recipient registers
		/// for.</typeparam>
		/// <param name="recipient">The recipient that will receive the messages.</param>
		/// <param name="token">A token for a messaging channel. If a recipient registers
		/// using a token, and a sender sends a message using the same token, then this
		/// message will be delivered to the recipient. Other recipients who did not
		/// use a token when registering (or who used a different token) will not
		/// get the message. Similarly, messages sent without any token, or with a different
		/// token, will not be delivered to that recipient.</param>
		/// <param name="receiveDerivedMessagesToo">If true, message types deriving from
		/// TMessage will also be transmitted to the recipient. For example, if a SendOrderMessage
		/// and an ExecuteOrderMessage derive from OrderMessage, registering for OrderMessage
		/// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
		/// and ExecuteOrderMessage to the recipient that registered.
		/// <para>Also, if TMessage is an interface, message types implementing TMessage will also be
		/// transmitted to the recipient. For example, if a SendOrderMessage
		/// and an ExecuteOrderMessage implement IOrderMessage, registering for IOrderMessage
		/// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
		/// and ExecuteOrderMessage to the recipient that registered.</para>
		/// </param>
		/// <param name="action">The action that will be executed when a message
		/// of type TMessage is sent.</param>
		public virtual void Register<TMessage>(
			object recipient,
			object token,
			bool receiveDerivedMessagesToo,
			Action<TMessage> action)
		{
			var messageType = typeof(TMessage);

			Dictionary<Type, List<WeakActionAndToken>> recipients;

			if (receiveDerivedMessagesToo)
			{
				if (_recipientsOfSubclassesAction == null)
				{
					_recipientsOfSubclassesAction = new Dictionary<Type, List<WeakActionAndToken>>();
				}

				recipients = _recipientsOfSubclassesAction;
			}
			else
			{
				if (_recipientsStrictAction == null)
				{
					_recipientsStrictAction = new Dictionary<Type, List<WeakActionAndToken>>();
				}

				recipients = _recipientsStrictAction;
			}

			List<WeakActionAndToken> list;

			if (!recipients.ContainsKey(messageType))
			{
				list = new List<WeakActionAndToken>();
				recipients.Add(messageType, list);
			}
			else
			{
				list = recipients[messageType];
			}

			var weakAction = new WeakAction<TMessage>(recipient, action);
			var item = new WeakActionAndToken
			{
				Action = weakAction,
				Token = token
			};
			list.Add(item);

			Cleanup();
		}

		/// <summary>
		/// Sends a message to registered recipients. The message will
		/// reach all recipients that registered for this message type
		/// using one of the Register methods.
		/// </summary>
		/// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
		/// <param name="message">The message to send to registered recipients.</param>
		public virtual void Send<TMessage>(TMessage message)
		{
			SendToTargetOrType(message, null, null);
		}

		/// <summary>
		/// Sends a message to registered recipients. The message will
		/// reach only recipients that registered for this message type
		/// using one of the Register methods, and that are
		/// of the targetType.
		/// </summary>
		/// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
		/// <typeparam name="TTarget">The type of recipients that will receive
		/// the message. The message won't be sent to recipients of another type.</typeparam>
		/// <param name="message">The message to send to registered recipients.</param>
		[SuppressMessage(
			"Microsoft.Design",
			"CA1004:GenericMethodsShouldProvideTypeParameter",
			Justification = "This syntax is more convenient than other alternatives.")]
		public virtual void Send<TMessage, TTarget>(TMessage message)
		{
			SendToTargetOrType(message, typeof(TTarget), null);
		}

		/// <summary>
		/// Sends a message to registered recipients. The message will
		/// reach only recipients that registered for this message type
		/// using one of the Register methods, and that are
		/// of the targetType.
		/// </summary>
		/// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
		/// <param name="message">The message to send to registered recipients.</param>
		/// <param name="token">A token for a messaging channel. If a recipient registers
		/// using a token, and a sender sends a message using the same token, then this
		/// message will be delivered to the recipient. Other recipients who did not
		/// use a token when registering (or who used a different token) will not
		/// get the message. Similarly, messages sent without any token, or with a different
		/// token, will not be delivered to that recipient.</param>
		public virtual void Send<TMessage>(TMessage message, object token)
		{
			SendToTargetOrType(message, null, token);
		}

		/// <summary>
		/// Unregisters a messager recipient completely. After this method
		/// is executed, the recipient will not receive any messages anymore.
		/// </summary>
		/// <param name="recipient">The recipient that must be unregistered.</param>
		public virtual void Unregister(object recipient)
		{
			UnregisterFromLists(recipient, _recipientsOfSubclassesAction);
			UnregisterFromLists(recipient, _recipientsStrictAction);
		}

		/// <summary>
		/// Unregisters a message recipient for a given type of messages only. 
		/// After this method is executed, the recipient will not receive messages
		/// of type TMessage anymore, but will still receive other message types (if it
		/// registered for them previously).
		/// </summary>
		/// <typeparam name="TMessage">The type of messages that the recipient wants
		/// to unregister from.</typeparam>
		/// <param name="recipient">The recipient that must be unregistered.</param>
		[SuppressMessage(
			"Microsoft.Design",
			"CA1004:GenericMethodsShouldProvideTypeParameter",
			Justification =
				"The type parameter TMessage identifies the message type that the recipient wants to unregister for.")]
		public virtual void Unregister<TMessage>(object recipient)
		{
			Unregister<TMessage>(recipient, null);
		}

		/// <summary>
		/// Unregisters a message recipient for a given type of messages and for
		/// a given action. Other message types will still be transmitted to the
		/// recipient (if it registered for them previously). Other actions that have
		/// been registered for the message type TMessage and for the given recipient (if
		/// available) will also remain available.
		/// </summary>
		/// <typeparam name="TMessage">The type of messages that the recipient wants
		/// to unregister from.</typeparam>
		/// <param name="recipient">The recipient that must be unregistered.</param>
		/// <param name="action">The action that must be unregistered for
		/// the recipient and for the message type TMessage.</param>
		public virtual void Unregister<TMessage>(object recipient, Action<TMessage> action)
		{
			UnregisterFromLists(recipient, action, _recipientsStrictAction);
			UnregisterFromLists(recipient, action, _recipientsOfSubclassesAction);
			Cleanup();
		}

		private static void CleanupList(IDictionary<Type, List<WeakActionAndToken>> lists)
		{
			if (lists == null)
			{
				return;
			}

			var listsToRemove = new List<Type>();
			foreach (var list in lists)
			{
				var recipientsToRemove = new List<WeakActionAndToken>();
				foreach (var item in list.Value)
				{
					if (item.Action == null
						|| !item.Action.IsAlive)
					{
						recipientsToRemove.Add(item);
					}
				}

				foreach (var recipient in recipientsToRemove)
				{
					list.Value.Remove(recipient);
				}

				if (list.Value.Count == 0)
				{
					listsToRemove.Add(list.Key);
				}
			}

			foreach (var key in listsToRemove)
			{
				lists.Remove(key);
			}
		}

		private static bool Implements(Type instanceType, Type interfaceType)
		{
			if (interfaceType == null
				|| instanceType == null)
			{
				return false;
			}

			var interfaces = instanceType.GetInterfaces();
			foreach (var currentInterface in interfaces)
			{
				if (currentInterface == interfaceType)
				{
					return true;
				}
			}

			return false;
		}

		private static void SendToList<TMessage>(
			TMessage message,
			IEnumerable<WeakActionAndToken> list,
			Type messageTargetType,
			object token)
		{
			if (list != null)
			{
				// Clone to protect from people registering in a "receive message" method
				// Bug correction Messaging BL0004.007
				var listClone = list.Take(list.Count()).ToList();

				foreach (var item in listClone)
				{
					var executeAction = item.Action as IExecuteWithObject;

					if (executeAction != null
						&& item.Action.IsAlive
						&& item.Action.Target != null
						&& (messageTargetType == null
							|| item.Action.Target.GetType() == messageTargetType
							|| Implements(item.Action.Target.GetType(), messageTargetType))
						&& ((item.Token == null && token == null)
							|| item.Token != null && item.Token.Equals(token)))
					{
						executeAction.ExecuteWithObject(message);
					}
				}
			}
		}

		private static void UnregisterFromLists(object recipient, Dictionary<Type, List<WeakActionAndToken>> lists)
		{
			if (recipient == null
				|| lists == null
				|| lists.Count == 0)
			{
				return;
			}

			lock (lists)
			{
				foreach (var messageType in lists.Keys)
				{
					foreach (var item in lists[messageType])
					{
						var weakAction = item.Action;

						if (weakAction != null
							&& recipient == weakAction.Target)
						{
							weakAction.MarkForDeletion();
						}
					}
				}
			}
		}

		private static void UnregisterFromLists<TMessage>(
			object recipient,
			Action<TMessage> action,
			Dictionary<Type, List<WeakActionAndToken>> lists)
		{
			var messageType = typeof(TMessage);

			if (recipient == null
				|| lists == null
				|| lists.Count == 0
				|| !lists.ContainsKey(messageType))
			{
				return;
			}

			lock (lists)
			{
				foreach (var item in lists[messageType])
				{
					var weakActionCasted = item.Action as WeakAction<TMessage>;

					if (weakActionCasted != null
						&& recipient == weakActionCasted.Target
						&& (action == null
							|| action == weakActionCasted.Action))
					{
						item.Action.MarkForDeletion();
					}
				}
			}
		}

		private void Cleanup()
		{
			CleanupList(_recipientsOfSubclassesAction);
			CleanupList(_recipientsStrictAction);
		}

		private void SendToTargetOrType<TMessage>(TMessage message, Type messageTargetType, object token)
		{
			var messageType = typeof(TMessage);

			if (_recipientsOfSubclassesAction != null)
			{

				var listClone = _recipientsOfSubclassesAction.Keys.Take(_recipientsOfSubclassesAction.Count()).ToList();

				foreach (var type in listClone)
				{
					List<WeakActionAndToken> list = null;

					if (messageType == type
						|| messageType.IsSubclassOf(type)
						|| Implements(messageType, type))
					{
						list = _recipientsOfSubclassesAction[type];
					}

					SendToList(message, list, messageTargetType, token);
				}
			}

			if (_recipientsStrictAction != null)
			{
				if (_recipientsStrictAction.ContainsKey(messageType))
				{
					var list = _recipientsStrictAction[messageType];
					SendToList(message, list, messageTargetType, token);
				}
			}

			Cleanup();
		}

		private struct WeakActionAndToken
		{
			public WeakAction Action;

			public object Token;
		}
	}

	/// <summary>
	/// Passes a generic value (Content) to a recipient.
	/// </summary>
	/// <typeparam name="T">The type of the Content property.</typeparam>
	////[ClassInfo(typeof(Messenger))]
	public class GenericMessage<T> : MessageBase
	{
		/// <summary>
		/// Initializes a new instance of the GenericMessage class.
		/// </summary>
		/// <param name="content">The message content.</param>
		public GenericMessage(T content)
		{
			Content = content;
		}

		/// <summary>
		/// Initializes a new instance of the GenericMessage class.
		/// </summary>
		/// <param name="sender">The message's sender.</param>
		/// <param name="content">The message content.</param>
		public GenericMessage(object sender, T content)
			: base(sender)
		{
			Content = content;
		}

		/// <summary>
		/// Initializes a new instance of the GenericMessage class.
		/// </summary>
		/// <param name="sender">The message's sender.</param>
		/// <param name="target">The message's intended target. This parameter can be used
		/// to give an indication as to whom the message was intended for. Of course
		/// this is only an indication, amd may be null.</param>
		/// <param name="content">The message content.</param>
		public GenericMessage(object sender, object target, T content)
			: base(sender, target)
		{
			Content = content;
		}

		/// <summary>
		/// Gets or sets the message's content.
		/// </summary>
		public T Content
		{
			get;
			protected set;
		}
	}

	public class WeakAction<T> : WeakAction, IExecuteWithObject
	{
		private readonly Action<T> _action;

		/// <summary>
		/// Initializes a new instance of the WeakAction class.
		/// </summary>
		/// <param name="target">The action's owner.</param>
		/// <param name="action">The action that will be associated to this instance.</param>
		public WeakAction(object target, Action<T> action)
			: base(target, null)
		{
			_action = action;
		}

		/// <summary>
		/// Gets the Action associated to this instance.
		/// </summary>
		public new Action<T> Action
		{
			get
			{
				return _action;
			}
		}

		/// <summary>
		/// Executes the action. This only happens if the action's owner
		/// is still alive. The action's parameter is set to default(T).
		/// </summary>
		public new void Execute()
		{
			if (_action != null
				&& IsAlive)
			{
				_action(default(T));
			}
		}

		/// <summary>
		/// Executes the action. This only happens if the action's owner
		/// is still alive.
		/// </summary>
		/// <param name="parameter">A parameter to be passed to the action.</param>
		public void Execute(T parameter)
		{
			if (_action != null
				&& IsAlive)
			{
				_action(parameter);
			}
		}

		/// <summary>
		/// Executes the action with a parameter of type object. This parameter
		/// will be casted to T. This method implements <see cref="IExecuteWithObject.ExecuteWithObject" />
		/// and can be useful if you store multiple WeakAction{T} instances but don't know in advance
		/// what type T represents.
		/// </summary>
		/// <param name="parameter">The parameter that will be passed to the action after
		/// being casted to T.</param>
		public void ExecuteWithObject(object parameter)
		{
			var parameterCasted = (T)parameter;
			Execute(parameterCasted);
		}
	}

	public class WeakAction
	{
		private readonly Action _action;

		private WeakReference _reference;

		/// <summary>
		/// Initializes a new instance of the <see cref="WeakAction" /> class.
		/// </summary>
		/// <param name="target">The action's owner.</param>
		/// <param name="action">The action that will be associated to this instance.</param>
		public WeakAction(object target, Action action)
		{
			_reference = new WeakReference(target);
			_action = action;
		}

		/// <summary>
		/// Gets the Action associated to this instance.
		/// </summary>
		public Action Action
		{
			get
			{
				return _action;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the Action's owner is still alive, or if it was collected
		/// by the Garbage Collector already.
		/// </summary>
		public bool IsAlive
		{
			get
			{
				if (_reference == null)
				{
					return false;
				}

				return _reference.IsAlive;
			}
		}

		/// <summary>
		/// Gets the Action's owner. This object is stored as a <see cref="WeakReference" />.
		/// </summary>
		public object Target
		{
			get
			{
				if (_reference == null)
				{
					return null;
				}

				return _reference.Target;
			}
		}

		/// <summary>
		/// Executes the action. This only happens if the action's owner
		/// is still alive.
		/// </summary>
		public void Execute()
		{
			if (_action != null
				&& IsAlive)
			{
				_action();
			}
		}

		/// <summary>
		/// Sets the reference that this instance stores to null.
		/// </summary>
		public void MarkForDeletion()
		{
			_reference = null;
		}
	}

	public interface IExecuteWithObject
	{
		void ExecuteWithObject(object parameter);
	}

	/// <summary>
	/// The Messenger is a class allowing objects to exchange messages.
	/// </summary>
	////[ClassInfo(typeof(Messenger))]
	public interface IMessenger
	{
		/// <summary>
		/// Registers a recipient for a type of message TMessage. The action
		/// parameter will be executed when a corresponding message is sent.
		/// <para>Registering a recipient does not create a hard reference to it,
		/// so if this recipient is deleted, no memory leak is caused.</para>
		/// </summary>
		/// <typeparam name="TMessage">The type of message that the recipient registers
		/// for.</typeparam>
		/// <param name="recipient">The recipient that will receive the messages.</param>
		/// <param name="action">The action that will be executed when a message
		/// of type TMessage is sent.</param>
		void Register<TMessage>(object recipient, Action<TMessage> action);

		/// <summary>
		/// Registers a recipient for a type of message TMessage.
		/// The action parameter will be executed when a corresponding 
		/// message is sent. See the receiveDerivedMessagesToo parameter
		/// for details on how messages deriving from TMessage (or, if TMessage is an interface,
		/// messages implementing TMessage) can be received too.
		/// <para>Registering a recipient does not create a hard reference to it,
		/// so if this recipient is deleted, no memory leak is caused.</para>
		/// </summary>
		/// <typeparam name="TMessage">The type of message that the recipient registers
		/// for.</typeparam>
		/// <param name="recipient">The recipient that will receive the messages.</param>
		/// <param name="receiveDerivedMessagesToo">If true, message types deriving from
		/// TMessage will also be transmitted to the recipient. For example, if a SendOrderMessage
		/// and an ExecuteOrderMessage derive from OrderMessage, registering for OrderMessage
		/// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
		/// and ExecuteOrderMessage to the recipient that registered.
		/// <para>Also, if TMessage is an interface, message types implementing TMessage will also be
		/// transmitted to the recipient. For example, if a SendOrderMessage
		/// and an ExecuteOrderMessage implement IOrderMessage, registering for IOrderMessage
		/// and setting receiveDerivedMessagesToo to true will send SendOrderMessage
		/// and ExecuteOrderMessage to the recipient that registered.</para>
		/// </param>
		/// <param name="action">The action that will be executed when a message
		/// of type TMessage is sent.</param>
		void Register<TMessage>(object recipient, bool receiveDerivedMessagesToo, Action<TMessage> action);

		/// <summary>
		/// Sends a message to registered recipients. The message will
		/// reach all recipients that registered for this message type
		/// using one of the Register methods.
		/// </summary>
		/// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
		/// <param name="message">The message to send to registered recipients.</param>
		void Send<TMessage>(TMessage message);

		/// <summary>
		/// Sends a message to registered recipients. The message will
		/// reach only recipients that registered for this message type
		/// using one of the Register methods, and that are
		/// of the targetType.
		/// </summary>
		/// <typeparam name="TMessage">The type of message that will be sent.</typeparam>
		/// <typeparam name="TTarget">The type of recipients that will receive
		/// the message. The message won't be sent to recipients of another type.</typeparam>
		/// <param name="message">The message to send to registered recipients.</param>
		[SuppressMessage(
			"Microsoft.Design",
			"CA1004:GenericMethodsShouldProvideTypeParameter",
			Justification = "This syntax is more convenient than other alternatives.")]
		void Send<TMessage, TTarget>(TMessage message);

		/// <summary>
		/// Unregisters a messager recipient completely. After this method
		/// is executed, the recipient will not receive any messages anymore.
		/// </summary>
		/// <param name="recipient">The recipient that must be unregistered.</param>
		void Unregister(object recipient);

		/// <summary>
		/// Unregisters a message recipient for a given type of messages only. 
		/// After this method is executed, the recipient will not receive messages
		/// of type TMessage anymore, but will still receive other message types (if it
		/// registered for them previously).
		/// </summary>
		/// <typeparam name="TMessage">The type of messages that the recipient wants
		/// to unregister from.</typeparam>
		/// <param name="recipient">The recipient that must be unregistered.</param>
		[SuppressMessage(
			"Microsoft.Design",
			"CA1004:GenericMethodsShouldProvideTypeParameter",
			Justification = "This syntax is more convenient than other alternatives.")]
		void Unregister<TMessage>(object recipient);

		/// <summary>
		/// Unregisters a message recipient for a given type of messages and for
		/// a given action. Other message types will still be transmitted to the
		/// recipient (if it registered for them previously). Other actions that have
		/// been registered for the message type TMessage and for the given recipient (if
		/// available) will also remain available.
		/// </summary>
		/// <typeparam name="TMessage">The type of messages that the recipient wants
		/// to unregister from.</typeparam>
		/// <param name="recipient">The recipient that must be unregistered.</param>
		/// <param name="action">The action that must be unregistered for
		/// the recipient and for the message type TMessage.</param>
		void Unregister<TMessage>(object recipient, Action<TMessage> action);
	}
}
